﻿#include "ForegroundMeshInstances.h"

#include "Render/PreRender/PreRenderMesh.h"
#include "Render/Mesh/VulkanMesh.h"

void CVulkanForegroundMeshInstances::PreProcess(
    const TVector<SPreRenderForegroundMeshInstance> &InDatas)
{
    Instances.Resize(InDatas.Num());
    for (size_t i = 0; i < InDatas.size(); i++)
    {
        Instances[i].BindData(InDatas[i]);
    }


    //统计描述符
    {
        SVulkanDescriptorPoolSize PoolSize;
        PoolSize.SetNum=Instances.Num();
        PoolSize+=CVulkanContext::Get()->GetSceneGlobal()->ModelDescriptorSetLayout.DescriptorNums*Instances.Num();
        PoolSize.DescriptorNums.InputAttachmentDescriptorNum+=Instances.Num();
        CVulkanContext::Get()->GetAutoResetDescriptorPool()->GenerallPool.PoolSize+=PoolSize;
    }

    {
        //把最终结果绘制到屏幕上
        SVulkanDescriptorPoolSize PoolSize;
        PoolSize.SetNum= 1;
        PoolSize+=CVulkanContext::Get()->GetImageWidgetDescriptorSetLayout().DescriptorNums;
        CVulkanContext::Get()->GetAutoResetDescriptorPool()->GenerallPool.PoolSize+=PoolSize;
    }        


}


void CVulkanForegroundMeshInstances::RecordCommand(
    vk::CommandBuffer CommandBuffer
    , const SMatrix4x4 &InViewMatrix
    , const SMatrix4x4 &InProjectionMatrix
    , vk::Extent2D InViewportSize
    )
{
    
    CVulkanContext::Get()->GetForegroundMeshPipeline()->UpdateFrameBuffer(InViewportSize);

    //begin render pass
    vk::ClearValue ClearValues[2];
    ClearValues[0].color.float32[0]=0.2f;
    ClearValues[0].color.float32[1]=0.2f;
    ClearValues[0].color.float32[2]=0.2f;
    ClearValues[0].color.float32[3]=0.0f;

    ClearValues[1].depthStencil.depth=1.0f;
    ClearValues[1].depthStencil.stencil=0;

    vk::Rect2D ForgroundRenderArea=vk::Rect2D()
        .setOffset(vk::Offset2D(0,0))
        .setExtent(InViewportSize);

    auto RenderPassBeginInfo=vk::RenderPassBeginInfo()
        .setRenderPass(CVulkanContext::Get()->GetForegroundMeshPipeline()->AxisRenderPass)
        .setFramebuffer(CVulkanContext::Get()->GetForegroundMeshPipeline()->AxisFramebuffer)
        .setRenderArea(ForgroundRenderArea)
        .setClearValueCount(2)
        .setPClearValues(ClearValues);
    
    CommandBuffer.beginRenderPass(RenderPassBeginInfo, vk::SubpassContents::eInline);


    SForegroundMeshPipeline::SAxisItemVertexPushConstant PushConstantValue;
    SMatrix4x4 VewProjectionMatrix=InProjectionMatrix*InViewMatrix;
    VewProjectionMatrix.ToFloatArray(PushConstantValue.ProjectionView);

    //set viewport
    vk::Viewport Viewport=vk::Viewport()
        .setX(0)
        .setY(0)
        .setWidth(InViewportSize.width)
        .setHeight(InViewportSize.height)
        .setMinDepth(0.0f)
        .setMaxDepth(1.0f);

    CommandBuffer.setViewport(0, Viewport);

    //bind pipeline
    CommandBuffer.bindPipeline(
        vk::PipelineBindPoint::eGraphics
        , CVulkanContext::Get()->GetForegroundMeshPipeline()->AxisPipeline);

    vk::Rect2D ForgroundClipRect;
    ForgroundClipRect.offset=vk::Offset2D(0,0);
    ForgroundClipRect.extent=InViewportSize;
    CommandBuffer.setScissor(0,1,&ForgroundClipRect);

    CommandBuffer.pushConstants(
        CVulkanContext::Get()->GetForegroundMeshPipeline()->AxisPipelineLayout
        , vk::ShaderStageFlagBits::eVertex
        , 0, sizeof(SForegroundMeshPipeline::SAxisItemVertexPushConstant), &PushConstantValue);

    for(auto& Instance: Instances)
    {
        Instance.RecordCommand(
            CommandBuffer
            ,InViewMatrix
            ,InProjectionMatrix
            ,InViewportSize
            );
    }

    CommandBuffer.endRenderPass();
}

CVulkanForegroundMeshInstance::CVulkanForegroundMeshInstance()
{
    ModelUniformBuffer=std::make_shared<TVulkanUniformBuffer<SForegroundVertexModelUniform>>();
}

void CVulkanForegroundMeshInstance::BindData(const SPreRenderForegroundMeshInstance &InData)
{
    Position=InData.Position;
    Rotation=InData.Rotation;
    Scale=InData.Scale;
    Mesh=CVulkanContext::Get()->ToVulkanMesh(InData.Mesh->WriteInData);
    Color=InData.Color;
    ViewPosition=InData.ViewPosition;
}

void CVulkanForegroundMeshInstance::RecordCommand(
    vk::CommandBuffer CommandBuffer
    , const SMatrix4x4 &InViewMatrix
    , const SMatrix4x4 &InProjectionMatrix
    , vk::Extent2D InViewportSize
    )
{


    SForegroundVertexModelUniform ModelUniform;
    SMatrix4x4 Model(Position
        ,Rotation
        ,Scale
        );
    Model.ToFloatArray(ModelUniform.Model);
    ModelUniform.Color[0]= Color.R;
    ModelUniform.Color[1]= Color.G;
    ModelUniform.Color[2]= Color.B;
    ModelUniform.Color[3]= Color.A;

    ModelUniform.ViewPosition[0]=ViewPosition.X;
    ModelUniform.ViewPosition[1]=ViewPosition.Y;
    ModelUniform.ViewPosition[2]=ViewPosition.Z;
    

    ModelUniformBuffer->WriteData(ModelUniform);

    vk::DescriptorSet ModelDescriptorSet;
    {
        //allocate descriptor set
        TVector<vk::DescriptorSetLayout> DescriptorSetLayouts=
        {
            CVulkanContext::Get()->GetForegroundMeshPipeline()->DescriptorSetLayout
        };
        
        vk::DescriptorSetAllocateInfo DescriptorSetAllocateInfo = vk::DescriptorSetAllocateInfo()
            .setDescriptorPool(CVulkanContext::Get()->GetAutoResetDescriptorPool()->GenerallPool.Pool)
            .setSetLayouts(DescriptorSetLayouts);
        
        ModelDescriptorSet = CVulkanContext::Get()->GetDevice().allocateDescriptorSets(DescriptorSetAllocateInfo) [0];


        //update descriptor set
        vk::DescriptorBufferInfo DescriptorBufferInfo = vk::DescriptorBufferInfo()
            .setBuffer(ModelUniformBuffer->GetBuffer())
            .setOffset(0)
            .setRange(sizeof(SForegroundVertexModelUniform));
        
        vk::WriteDescriptorSet WriteDescriptorSet = vk::WriteDescriptorSet()
            .setDstSet(ModelDescriptorSet)
            .setDstBinding(0)
            .setDstArrayElement(0)
            .setDescriptorType(vk::DescriptorType::eUniformBuffer)
            .setDescriptorCount(1)
            .setPBufferInfo(&DescriptorBufferInfo);
        
        CVulkanContext::Get()->GetDevice().updateDescriptorSets(WriteDescriptorSet, nullptr);
    }

    CommandBuffer.bindDescriptorSets(
        vk::PipelineBindPoint::eGraphics
        , CVulkanContext::Get()->GetForegroundMeshPipeline()->AxisPipelineLayout
        , 0
        , {ModelDescriptorSet}
        , nullptr);

    Mesh->RecordDrawCommands(CommandBuffer);

}
